package com.netx.data;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import com.netx.generics.basic.IntegrityException;
import com.netx.generics.time.Date;
import com.netx.generics.time.Moment;
import com.netx.generics.time.Time;
import com.netx.generics.util.Strings;


public class Field {

	// TYPE
	public enum TYPE {
		BOOLEAN,
		BYTE,
		SHORT,
		INT,
		LONG,
		FLOAT,
		DOUBLE,
		TEXT,
		BINARY,
		DATE,
		TIME,
		MOMENT,
		FOREIGN_KEY
	}
	
	public enum CONSTRAINT {
		NO_ACTION,
		RESTRICT,
		CASCADE,
		SET_NULL,
		SET_DEFAULT
	}
	
	private final String _name;
	private final TYPE _type;
	private final boolean _required;
	private final CONSTRAINT _onDelete;
	private final CONSTRAINT _onUpdate;
	private final Object _default;
	private final Pattern _validation;
	private final long _length;
	private final MetaData _holder;
	private final MetaData _foreignEntity;

	Field(String name, TYPE type, boolean required, Object defaultValue, CONSTRAINT onDelete, CONSTRAINT onUpdate, Pattern validation, long length, MetaData holder, MetaData foreignEntity) {
		_name = name;
		_type = type;
		_required = required;
		_onDelete = onDelete;
		_onUpdate = onUpdate;
		_length = length;
		_default = defaultValue;
		_validation = validation;
		_holder = holder;
		_foreignEntity = foreignEntity;
	}

	public String toString() {
		return getName();
	}

	public String getName() {
		return _name;
	}
	
	public TYPE getType() {
		return _type;
	}
	
	public MetaData getHolderEntity() {
		return _holder;
	}
	
	public boolean isRequired() {
		return _required;
	}
	
	public Object getDefaultValue() {
		if(_default == null) {
			return null;
		}
		else {
			if(_type == TYPE.MOMENT) {
				if(_default.toString().equals("now")) {
					return new Moment();
				}
			}
			else if(_type == TYPE.DATE) {
				if(_default.toString().equals("now")) {
					return new Moment().getDate();
				}
			}
			else if(_type == TYPE.TIME) {
				if(_default.toString().equals("now")) {
					return new Moment().getTime();
				}
			}
			return _default;
		}
	}
	
	public MetaData getForeignEntity() {
		if(getType() != TYPE.FOREIGN_KEY) {
			throw new UnsupportedOperationException("this field ("+getName()+") is not a foreign key");
		}
		else {
			return _foreignEntity;
		}
	}
	
	public CONSTRAINT getOnDeleteConstraint() {
		if(getType() != TYPE.FOREIGN_KEY) {
			throw new UnsupportedOperationException("this field ("+getName()+") is not a foreign key");
		}
		else {
			return _onDelete;
		}
	}

	public CONSTRAINT getOnUpdateConstraint() {
		if(getType() == TYPE.FOREIGN_KEY) {
			throw new UnsupportedOperationException("this field is not a foreign key");
		}
		else {
			return _onUpdate;
		}
	}
	
	// For MetaData.valueToSQL
	// throws:
	// IllegalArgumentException if the value has the wrong format
	// ClassCastException if the value is of an unexpected type
	// IndexOutOfBoundsException if the value is binary or String and the length is off limits
	String toSQL(Object value) {
		if(_validation != null) {
			Matcher m = _validation.matcher(value.toString());
			if(!m.matches()) {
				throw new IllegalArgumentException(value.toString());
			}
		}
		if(_type == TYPE.BOOLEAN) {
			if(value instanceof String) {
				if(value.equals("1")) {
					return "1";
				}
				else if(value.equals("0")) {
					return "0";
				}
				else {
					throw new IllegalArgumentException(value.toString());
				}
			}
			else {
				return ((Boolean)value).booleanValue() ? "1" : "0";
			}
		}
		else if(_type == TYPE.BYTE) {
			return (value instanceof String) ? new Byte(value.toString()).toString() : ((Byte)value).toString();
		}
		else if(_type == TYPE.SHORT) {
			return (value instanceof String) ? new Short(value.toString()).toString() : ((Short)value).toString();
		}
		else if(_type == TYPE.INT) {
			return (value instanceof String) ? new Integer(value.toString()).toString() : ((Integer)value).toString();
		}
		else if(_type == TYPE.LONG) {
			return (value instanceof String) ? new Long(value.toString()).toString() : ((Long)value).toString();
		}
		else if(_type == TYPE.FLOAT) {
			return (value instanceof String) ? new Float(value.toString()).toString() : ((Float)value).toString();
		}
		else if(_type == TYPE.DOUBLE) {
			return (value instanceof String) ? new Double(value.toString()).toString() : ((Double)value).toString();
		}
		else if(_type == TYPE.MOMENT) {
			if(value instanceof String) {
				return "\""+Moment.valueOf(value.toString(), getHolderEntity().getDatabase().getDriver().getDateTimeFormat()).toString()+"\"";
			}
			else {
				return "\""+((Moment)value).format(getHolderEntity().getDatabase().getDriver().getDateTimeFormat())+"\"";
			}
		}
		else if(_type == TYPE.DATE) {
			if(value instanceof String) {
				return "\""+Date.valueOf(value.toString(), getHolderEntity().getDatabase().getDriver().getDateFormat()).toString()+"\"";
			}
			else {
				return "\""+((Date)value).format(getHolderEntity().getDatabase().getDriver().getDateFormat())+"\"";
			}
		}
		else if(_type == TYPE.TIME) {
			if(value instanceof String) {
				return "\""+Time.valueOf(value.toString(), getHolderEntity().getDatabase().getDriver().getTimeFormat()).toString()+"\"";
			}
			else {
				return "\""+((Time)value).format(getHolderEntity().getDatabase().getDriver().getTimeFormat())+"\"";
			}
		}
		else if(_type == TYPE.BINARY || _type == TYPE.TEXT) {
			String str = (String)value;
			if(str.length() > _length) {
				throw new IndexOutOfBoundsException(_length+"");
			}
			if(_type == TYPE.TEXT) {
				str = Strings.addSlashes(str);
			}
			return "\""+str+"\"";
		}
		else if(_type == TYPE.FOREIGN_KEY) {
			Field primaryKey = this.getForeignEntity().getPrimaryKey()[0];
			return primaryKey.toSQL(value);
		}
		else {
			throw new IntegrityException(_type);
		}
	}
}
